import json
import math
import random
import openpyxl
import copy
from scipy import integrate
import matplotlib.pyplot as plt
from django.http import JsonResponse
from course.models import *
from scipy import optimize
import numpy as np

static_file_path = 'static/files/'


class BaseModel:

    def __init__(self):
        # 0平均 1正态
        self.mode = 0
        self.param1 = 0
        self.param2 = 1

    def set_value(self, params):
        for key in params:
            try:
                setattr(self, key, float(params[key]))
            except Exception:
                pass

    def set_f(self, mode, params):
        self.mode = mode
        if self.mode <= 1:
            self.param1 = params[0]
            self.param2 = params[1]
        else:
            return NotImplementedError

    def f_ni(self, y):
        if self.mode == 0:
            return self.average_f_ni(y)
        elif self.mode == 1:
            return self.uniform_f_ni(y)
        else:
            return NotImplementedError

    def f(self, x):
        if self.mode == 0:
            return self.average_f(x)
        elif self.mode == 1:
            return self.uniform_f(x)
        else:
            return NotImplementedError

    def average_f_ni(self, y):
        if y < 0 or y > 1:
            return ValueError
        else:
            return (1 - y) * (self.param2 - self.param1) + self.param1

    def uniform_f_ni(self, y):
        if y < 0 or y > 1:
            return ValueError
        else:
            left = -1000000
            right = 1000000
            eps = 0.0001
            while left + eps < right:
                mid = (left + right) / 2.0
                if self.uniform_f(mid) < y:
                    left = mid
                else:
                    right = mid
            return left

    def average_f(self, x):
        if x < self.param1:
            return 0
        elif x > self.param2:
            return 1
        else:
            return (x - self.param1) / (self.param2 - self.param1)

    def uniform_f(self, x):
        x = (x - self.param1) / self.param2
        return (1.0 + math.erf(x / math.sqrt(2.0))) / 2.0

    def gen_data(self):
        raise NotImplementedError

    def get_file_name(self, suffix='.jpg'):
        x = random.randint(1, 10000)
        file_name = datetime.now().strftime('%m_%d_%y_%H_%M_' + str(x) + suffix)
        return static_file_path + 'calc_pictures/' + file_name

    def get_big_f_simulate(self, l, r):
        cnt = 100
        ans = 0
        width = (r - l) / cnt
        for i in range(cnt):
            ans += ((self.f(l + width * i) + self.f(l + width * (i + 0.5)) * 4
                    + self.f(l + width * (i + 1))) / 6) * width
        return ans


class Test1(BaseModel):

    def __init__(self):
        super().__init__()
        self.a = 2
        self.b = 3

    def gen_data(self):
        data = dict()
        values = dict()
        values['c'] = self.a * self.b
        data['values'] = values
        return data


# 批发价格契约 采用正态分布
class WholesalePriceContract(BaseModel):

    def __init__(self):
        super().__init__()
        self.set_f(mode=1, params=[100, 20])
        self.p = 1
        self.cs = 0.5
        self.cr = 0.25
        self.gs = 0.2
        self.gr = 0.2
        self.v = 0
        self.w = 0.6

    def get_s(self, q):
        cnt = 100
        ans = q
        for i in range(cnt):
            ans -= q / cnt * (self.f(i * q / cnt) + self.f((i + 0.5) * q / cnt) * 4 +
                              self.f((i + 1) * q / cnt)) / 6
        return ans

    def calc(self, q, q0):
        ans = dict()
        ans['qstar'] = q
        sq = self.get_s(q)
        ans['sq'] = sq
        ans['pir'] = (self.p - self.v + self.gr) * sq - (self.cr - self.v) * q \
                     - self.gr * self.param1 - self.w * q
        ans['pis'] = self.gs * sq + (self.w - self.cs) * q - self.gs * self.param1
        ans['pi'] = ans['pir'] + ans['pis']
        ans['q0'] = q0
        ans['piq0'] = (self.p - self.v + self.gr + self.gs) * self.get_s(ans['q0']) \
                        - (self.cr + self.cs - self.v) * ans['q0'] - (self.gr + self.gs) * self.param1
        return ans

    def get_pi_for_q(self):
        x = list()
        n = 20
        for i in range(n):
            q = self.f_ni((i + 1) / (n + 1))
            x.append(q)
        y1 = list()
        y2 = list()
        y3 = list()
        y4 = list()
        for i in range(n):
            values = self.calc(x[i], x[i])
            y1.append(values['pi'])
            y2.append(values['piq0'])
            y3.append(values['pis'])
            y4.append(values['pir'])
        plt.plot(x, y1, label='total')
        # plt.plot(x, y2, label='cooperate')
        plt.plot(x, y3, label='supplier')
        plt.plot(x, y4, label='retailer')
        plt.legend()
        file_name = self.get_file_name()
        plt.title('π-q')
        plt.xlabel('q')
        plt.ylabel('π')
        plt.savefig(file_name)
        plt.close()
        return file_name

    def gen_data(self):
        data = dict()
        qstar = self.f_ni(1 - (self.w + self.cr - self.v) / (self.p - self.v + self.gr))
        q0 = self.f_ni(1 - (self.cs + self.cr - self.v) / (self.p - self.v + self.gr + self.gs))
        data['values'] = self.calc(qstar, q0)
        pic_list = list()
        pic_list.append(self.get_pi_for_q())
        data['urls'] = pic_list
        return data


# 回购契约
class BuybackContract(BaseModel):
    def __init__(self):
        super().__init__()
        self.set_f(mode=1, params=[100, 20])
        self.p = 1
        self.cs = 0.5
        self.cr = 0.25
        self.gs = 0.2
        self.gr = 0.2
        self.v = 0
        self.w = 0.6
        self.b = 0.6

    def get_s(self, q):
        cnt = 100
        ans = q
        for i in range(cnt):
            ans -= q / cnt * (self.f(i * q / cnt) + self.f((i + 0.5) * q / cnt) * 4 +
                              self.f((i + 1) * q / cnt)) / 6
        return ans

    def calc(self, q):
        ans = dict()
        ans['qstar'] = q
        sq = self.get_s(q)
        ans['sq'] = sq
        ans['wbb'] = self.b + self.cs - \
                       (self.cs + self.cr - self.v) * (self.b + self.gs) / (self.p - self.v + self.gs + self.gr)
        ans['pir'] = (self.p - self.v + self.gr - self.b) * sq - (self.cr - self.v + ans['wbb'] - self.b) * q \
                     - self.gr * self.param1
        ans['pis'] = (self.gs + self.b) * sq - (self.cs - ans['wbb'] + self.b) * q - self.gs * self.param1
        ans['pi'] = ans['pir'] + ans['pis']
        return ans

    def get_pi_for_q(self):
        x = list()
        n = 20
        for i in range(n):
            q = self.f_ni((i + 1) / (n + 1))
            x.append(q)
        y1 = list()
        y3 = list()
        y4 = list()
        for i in range(n):
            values = self.calc(x[i])
            y1.append(values['pi'])
            y3.append(values['pis'])
            y4.append(values['pir'])

        plt.plot(x, y1, label='total')
        # plt.plot(x, y2, label='cooperate')
        plt.plot(x, y3, label='supplier')
        plt.plot(x, y4, label='retailer')
        plt.legend()
        file_name = self.get_file_name()
        plt.title('π-q')
        plt.xlabel('q')
        plt.ylabel('π')
        plt.savefig(file_name)
        plt.close()
        return file_name

    def gen_data(self):
        data = dict()
        qstar = self.f_ni(1 - (self.cs + self.cr - self.v) / (self.p - self.v + self.gr + self.gs))
        data['values'] = self.calc(qstar)
        pic_list = list()
        pic_list.append(self.get_pi_for_q())
        data['urls'] = pic_list
        return data


# 收益共享契约
class RevenueSharingContract(BaseModel):
    def __init__(self):
        super().__init__()
        self.set_f(mode=1, params=[100, 20])
        self.p = 1
        self.cs = 0.5
        self.cr = 0.25
        self.gs = 0.2
        self.gr = 0.2
        self.v = 0
        self.w = 0.6
        self.phi = 0.4

    def get_s(self, q):
        cnt = 100
        ans = q
        for i in range(cnt):
            ans -= q / cnt * (self.f(i * q / cnt) + self.f((i + 0.5) * q / cnt) * 4 +
                              self.f((i + 1) * q / cnt)) / 6
        return ans

    def calc(self, q):
        ans = dict()
        ans['qstar'] = q
        sq = self.get_s(q)
        ans['sq'] = sq
        ans['wr'] = -self.cr + self.phi * self.v + (self.cs + self.cr - self.v) * \
                    (self.phi * (self.p - self.v) + self.gr) / (self.p - self.v + self.gs + self.gr)
        ans['pi'] = (self.p - self.v + self.gr + self.gs) * self.get_s(q) \
                        - (self.cr + self.cs - self.v) * q - (self.gr + self.gs) * self.param1
        ans['pir'] = (self.phi * (self.p - self.v) + self.gr) * sq - \
                     (ans['wr'] + self.cr - self.phi * self.v) * q - self.gr * self.param1
        ans['pis'] = ans['pi'] - ans['pir']
        return ans

    def get_pi_for_q(self):
        x = list()
        n = 20
        for i in range(n):
            q = self.f_ni((i + 1) / (n + 1))
            x.append(q)
        y1 = list()
        y3 = list()
        y4 = list()
        for i in range(n):
            values = self.calc(x[i])
            y1.append(values['pi'])
            y3.append(values['pis'])
            y4.append(values['pir'])
        plt.plot(x, y1, label='total')
        # plt.plot(x, y2, label='cooperate')
        plt.plot(x, y3, label='supplier')
        plt.plot(x, y4, label='retailer')
        plt.legend()
        file_name = self.get_file_name()
        plt.title('π-q')
        plt.xlabel('q')
        plt.ylabel('π')
        plt.savefig(file_name)
        plt.close()
        return file_name

    def gen_data(self):
        data = dict()
        qstar = self.f_ni(1 - (self.cs + self.cr - self.v) / (self.p - self.v + self.gr + self.gs))
        data['values'] = self.calc(qstar)
        pic_list = list()
        pic_list.append(self.get_pi_for_q())
        data['urls'] = pic_list
        return data


# 数量柔性契约
class QuantityFlexibleContract(BaseModel):
    def __init__(self):
        super().__init__()
        self.set_f(mode=1, params=[100, 20])
        self.p = 1
        self.cs = 0.5
        self.cr = 0.25
        self.gs = 0.2
        self.gr = 0.2
        self.v = 0
        self.w = 0.6
        self.ipsilong = 0.15

    def get_s(self, q):
        cnt = 100
        ans = q
        for i in range(cnt):
            ans -= q / cnt * (self.f(i * q / cnt) + self.f((i + 0.5) * q / cnt) * 4 +
                              self.f((i + 1) * q / cnt)) / 6
        return ans

    def calc(self, q):
        ans = dict()
        ans['qstar'] = q
        sq = self.get_s(q)
        ans['sq'] = sq
        ans['wq'] = (self.p - self.v + self.gr) * (1 - self.f(q)) / \
                    (1 - self.f(q) + (1 - self.ipsilong) * self.f((1 - self.ipsilong) * q)) \
                    - self.cr + self.v
        ans['pi'] = (self.p - self.v + self.gr + self.gs) * self.get_s(q) \
                    - (self.cr + self.cs - self.v) * q - (self.gr + self.gs) * self.param1
        ans['pir'] = (self.p - self.v + self.gr) * sq - (ans['wq'] + self.cr - self.v) * q \
                    + (ans['wq'] + self.cr - self.v) * self.get_big_f_simulate((1 - self.ipsilong) * q, q) \
                    - self.param1 * self.gr
        ans['pis'] = ans['pi'] - ans['pir']
        return ans

    def get_pi_for_q(self):
        x = list()
        n = 20
        for i in range(n):
            q = self.f_ni((i + 1) / (n + 1))
            x.append(q)
        y1 = list()
        y3 = list()
        y4 = list()
        for i in range(n):
            values = self.calc(x[i])
            y1.append(values['pi'])
            y3.append(values['pis'])
            y4.append(values['pir'])
        plt.plot(x, y1, label='total')
        # plt.plot(x, y2, label='cooperate')
        plt.plot(x, y3, label='supplier')
        plt.plot(x, y4, label='retailer')
        plt.legend()
        file_name = self.get_file_name()
        plt.title('π-q')
        plt.xlabel('q')
        plt.ylabel('π')
        plt.savefig(file_name)
        plt.close()
        return file_name

    def gen_data(self):
        data = dict()
        qstar = self.f_ni(1 - (self.cs + self.cr - self.v) / (self.p - self.v + self.gr + self.gs))
        data['values'] = self.calc(qstar)
        pic_list = list()
        pic_list.append(self.get_pi_for_q())
        data['urls'] = pic_list
        return data


# 销售回扣契约
class SaleRebateContract(BaseModel):
    def __init__(self):
        super().__init__()
        self.set_f(mode=1, params=[100, 20])
        self.p = 1
        self.cs = 0.5
        self.cr = 0.25
        self.gs = 0.2
        self.gr = 0.2
        self.v = 0
        self.w = 0.6
        self.t = 0.85
        self.r = 0.5

    def get_s(self, q):
        cnt = 100
        ans = q
        for i in range(cnt):
            ans -= q / cnt * (self.f(i * q / cnt) + self.f((i + 0.5) * q / cnt) * 4 +
                              self.f((i + 1) * q / cnt)) / 6
        return ans

    def calc(self, q):
        ans = dict()
        ans['qstar'] = q
        sq = self.get_s(q)
        ans['sq'] = sq
        ans['wsr'] = (self.p - self.v + self.gr + self.r) * (1 - self.f(q)) - self.cr + self.v
        ans['pi'] = (self.p - self.v + self.gr + self.gs) * self.get_s(q) \
                    - (self.cr + self.cs - self.v) * q - (self.gr + self.gs) * self.param1
        ans['pir'] = ans['pi'] + self.gs * (self.param1 - sq + q * (1 - self.f(q))) \
                    + self.r * (q * self.f(q) - self.t * q - self.get_big_f_simulate(self.t * q, q))
        ans['pis'] = ans['pi'] - ans['pir']
        return ans

    def get_pi_for_q(self):
        x = list()
        n = 20
        for i in range(n):
            q = self.f_ni((i + 1) / (n + 1))
            x.append(q)
        y1 = list()
        y3 = list()
        y4 = list()
        for i in range(n):
            values = self.calc(x[i])
            y1.append(values['pi'])
            y3.append(values['pis'])
            y4.append(values['pir'])

        plt.plot(x, y1, label='total')
        # plt.plot(x, y2, label='cooperate')
        plt.plot(x, y3, label='supplier')
        plt.plot(x, y4, label='retailer')
        plt.legend()
        file_name = self.get_file_name()
        plt.title('π-q')
        plt.xlabel('q')
        plt.ylabel('π')
        plt.savefig(file_name)
        plt.close()
        return file_name

    def gen_data(self):
        data = dict()
        qstar = self.f_ni(1 - (self.cs + self.cr - self.v) / (self.p - self.v + self.gr + self.gs))
        data['values'] = self.calc(qstar)
        pic_list = list()
        pic_list.append(self.get_pi_for_q())
        data['urls'] = pic_list
        return data


# 数量折扣契约
class QuantityDiscountContract(BaseModel):
    def __init__(self):
        super().__init__()
        self.set_f(mode=1, params=[100, 20])
        self.p = 1
        self.cs = 0.5
        self.cr = 0.25
        self.gs = 0.2
        self.gr = 0.2
        self.v = 0
        self.w = 0.6
        self.lamda = 0.5

    def get_s(self, q):
        cnt = 100
        ans = q
        for i in range(cnt):
            ans -= q / cnt * (self.f(i * q / cnt) + self.f((i + 0.5) * q / cnt) * 4 +
                              self.f((i + 1) * q / cnt)) / 6
        return ans

    def calc(self, q):
        ans = dict()
        ans['qstar'] = q
        sq = self.get_s(q)
        ans['sq'] = sq
        ans['wdq'] = ((1 - self.lamda) * (self.p - self.v + self.gs + self.gr) - self.gs) * sq / q + \
                       self.lamda * (self.cs + self.cr - self.v) - self.cr + self.v
        ans['pi'] = (self.p - self.v + self.gr + self.gs) * self.get_s(q) \
                    - (self.cr + self.cs - self.v) * q - (self.gr + self.gs) * self.param1
        ans['pir'] = self.lamda * (ans['pi'] + (self.gs + self.gr) * self.param1) - self.gr * self.param1
        ans['pis'] = ans['pi'] - ans['pir']
        return ans

    def get_pi_for_q(self):
        x = list()
        n = 20
        for i in range(n):
            q = self.f_ni((i + 1) / (n + 1))
            x.append(q)
        y1 = list()
        y3 = list()
        y4 = list()
        for i in range(n):
            values = self.calc(x[i])
            y1.append(values['pi'])
            y3.append(values['pis'])
            y4.append(values['pir'])

        plt.plot(x, y1, label='total')
        # plt.plot(x, y2, label='cooperate')
        plt.plot(x, y3, label='supplier')
        plt.plot(x, y4, label='retailer')
        plt.legend()
        file_name = self.get_file_name()
        plt.title('π-q')
        plt.xlabel('q')
        plt.ylabel('π')
        plt.savefig(file_name)
        plt.close()
        return file_name

    def gen_data(self):
        data = dict()
        qstar = self.f_ni(1 - (self.cs + self.cr - self.v) / (self.p - self.v + self.gr + self.gs))
        data['values'] = self.calc(qstar)
        pic_list = list()
        pic_list.append(self.get_pi_for_q())
        data['urls'] = pic_list
        return data


# NIM游戏
class NimGame(BaseModel):
    def __init__(self):
        super().__init__()
        self.x1 = 2
        self.x2 = 3
        self.x3 = 5

    def gen_data(self):
        data = dict()
        self.x1 = int(self.x1)
        self.x2 = int(self.x2)
        self.x3 = int(self.x3)
        if (self.x1 ^ self.x2 ^ self.x3) == 0:
            winner = 'Bob'
        else:
            winner = 'Alice'
        data['winner'] = winner
        return data


# 关键字搜索拍卖
class KeywordSearchAuction(BaseModel):
    def __init__(self):
        super().__init__()
        self.alpha1 = 2
        self.alpha2 = 3
        self.b1 = 3
        self.b2 = 5
        self.b3 = 8

    def set_data(self, data, pos, x, p):
        if pos == 1:
            data['x1'] = x
            data['p1'] = p
        elif pos == 2:
            data['x2'] = x
            data['p2'] = p
        elif pos == 3:
            data['x3'] = x
            data['p3'] = p

    def gen_data(self):
        data = dict()
        b = list()
        b.append([self.b1, 1])
        b.append([self.b2, 2])
        b.append([self.b3, 3])
        b.sort(key=lambda x: -x[0])
        alpha = list()
        alpha.append([self.alpha1, 1])
        alpha.append([self.alpha2, 2])
        alpha.sort(key=lambda x: -x[0])
        b[0].append(alpha[0][1])
        b[0].append(b[1][0] * (alpha[0][0] - alpha[1][0]) + b[2][0] * alpha[1][0])
        b[1].append(alpha[1][1])
        b[1].append(b[2][0] * alpha[1][0])
        b[2].append(0)
        b[2].append(0)
        for i in range(3):
            self.set_data(data, b[i][1], b[i][2], b[i][3])
        return data


# 整数01背包问题
class IntegerKnapsackProblem(BaseModel):
    def __init__(self):
        super().__init__()
        self.V = 9
        self.v1 = 3
        self.v2 = 4
        self.v3 = 5
        self.w1 = 8
        self.w2 = 10
        self.w3 = 12

    def gen_data(self):
        self.V = int(self.V)
        self.v1 = int(self.v1)
        self.v2 = int(self.v2)
        self.v3 = int(self.v3)
        data = dict()
        v = [self.v1, self.v2, self.v3]
        w = [self.w1, self.w2, self.w3]
        x = [0] * 3
        dp = [[0 for _ in range(self.V + 1)] for _ in range(4)]
        sol = [[0 for _ in range(self.V + 1)] for _ in range(4)]
        for i in range(1, 4):
            for j in range(self.V + 1):
                if j >= v[i - 1] and dp[i - 1][j] < dp[i - 1][j - v[i - 1]] + w[i - 1]:
                    dp[i][j] = dp[i - 1][j - v[i - 1]] + w[i - 1]
                    sol[i][j] = j - v[i - 1]
                else:
                    dp[i][j] = dp[i - 1][j]
                    sol[i][j] = j
        now = self.V
        pos = 3
        while pos > 0:
            if sol[pos][now] != now:
                x[pos - 1] = 1
            else:
                x[pos - 1] = 0
            now = sol[pos][now]
            pos -= 1
        for i in range(3):
            data['x' + str(i + 1)] = x[i]
        data['W'] = dp[3][self.V]
        return data


# 生产博弈算例
class ProductionGame(BaseModel):
    def __init__(self):
        super().__init__()
        self.set_f(mode=1, params=[100, 20])
        self.a = 15
        self.b = 0.1
        self.c = 1

    def calc(self):
        left = 0
        right = 1000000
        while left + 0.0001 < right:
            mid = (left + right) / 2
            if self.L(mid) > 0:
                left = mid
            else:
                right = mid
        return left

    def L(self, q):
        return self.a * math.exp(-self.b * 2 * q) - self.c - q * self.a * self.b * math.exp(-self.b * 2 * q)

    def get_L_for_qn(self, l, r):
        x = list()
        cnt = 100
        for i in range(cnt):
            x.append(l + i * (r - l) / cnt)
        y1 = list()
        for i in range(cnt):
            value = self.L(x[i])
            y1.append(value)

        plt.plot(x, y1, label='L(q)')
        # plt.plot(x, y2, label='cooperate')
        plt.legend()
        file_name = self.get_file_name()
        plt.title('L-q')
        plt.xlabel('q')
        plt.ylabel('L')
        plt.savefig(file_name)
        plt.close()
        return file_name

    def gen_data(self):
        data = dict()
        data['qn'] = self.calc()
        pic_list = list()
        pic_list.append(self.get_L_for_qn(0, data['qn'] * 2))
        data['urls'] = pic_list
        return data


# 价格博弈算例
class PricingGame(BaseModel):
    def __init__(self):
        super().__init__()
        self.set_f(mode=1, params=[100, 20])
        self.a = 15
        self.b = 2
        self.c = 1
        self.alpha = 0.5

    def calc(self):
        left = 0
        right = 1000000
        while left + 0.0001 < right:
            mid = (left + right) / 2
            if (self.react(mid, 1) - self.react(mid, 2)) * (self.react(left, 1) - self.react(left, 2)) > 0:
                left = mid
            else:
                right = mid
        return left, self.react(left, 1)

    def react(self, q, mode):
        left = 0
        right = 1000000
        while left + 0.0001 < right:
            mid = (left + right) / 2
            if mode == 1:
                value = self.L1(q, mid)
            else:
                value = self.L2(q, mid)
            if value < 0:
                right = mid
            else:
                left = mid
        return left

    def L1(self, w, m):
        return self.a - self.b * (w + m) ** self.alpha - m * self.alpha * (w + m) ** (self.alpha - 1)

    def L2(self, w, m):
        return self.a - self.b * (w + m) ** self.alpha - (w - self.c) * self.alpha * (w + m) ** (self.alpha - 1)

    def get_two_actions(self, l, r):
        x = list()
        cnt = 100
        for i in range(cnt):
            x.append(l + i * (r - l) / cnt)
        y1 = list()
        y2 = list()
        for i in range(cnt):
            value = self.react(x[i], 1)
            y1.append(value)
            value = self.react(x[i], 2)
            y2.append(value)

        plt.plot(x, y1, label='retailer')
        plt.plot(x, y2, label='supplier')
        plt.legend()
        file_name = self.get_file_name()
        plt.title('w-m reaction picture')
        plt.xlabel('w')
        plt.ylabel('m')
        plt.savefig(file_name)
        plt.close()
        return file_name

    def gen_data(self):
        data = dict()
        wstar, mstar = self.calc()
        data['wstar'] = wstar
        data['mstar'] = mstar
        pic_list = list()
        pic_list.append(self.get_two_actions(0, wstar * 2))
        data['urls'] = pic_list
        return data


# 地球球体计算误差
class SphereDistance(BaseModel):
    def __init__(self):
        super().__init__()
        self.r = 6371

    def solve(self):
        eps = [1e-2, 1e-3, 1e-4, 1e-5, 1e-6, 1e-7, 1e-8, 1e-9]
        a = list()
        for j in range(8):
            ans = 0
            iters = 20
            for i in range(iters):
                x0 = random.random() * math.pi / 2
                x1 = x0 + eps[j]
                y0 = random.random() * math.pi
                y1 = y0 + eps[j]
                p1 = self.d1(x0, y0, x1, y1)
                p2 = self.d2(x0, y0, x1, y1)
                ans += abs(p1 - p2) / max(p1, p2)
            print(format(ans / iters * 100, '.6f') + '%')
            a.append(ans / iters)


        plt.title('球体距离计算误差走势图', fontproperties='SimHei')
        plt.plot(a)
        plt.xticks([i for i in range(8)], ['1e-' + str(i + 2) for i in range(8)])
        plt.yticks([i / 5 for i in range(6)], [i * 20 for i in range(6)])
        plt.xlabel('距离/rad', fontproperties='SimHei')
        plt.ylabel('误差百分比/%', fontproperties='SimHei')
        file_name = self.get_file_name()
        plt.savefig(file_name)
        plt.close()
        return file_name

    def d1(self, x0, y0, x1, y1):
        return 2 * self.r * math.asin(self.scale(math.sqrt(math.sin((x0 - x1) / 2) ** 2 +
                                       math.cos(x0) * math.cos(x1) * math.sin((y0 - y1) / 2) ** 2)))

    def d2(self, x0, y0, x1, y1):
        return self.r * math.acos(self.scale(math.sin(x0) * math.sin(x1) + math.cos(x0) * math.cos(x1) * math.cos(y0 - y1)))

    def scale(self, value):
        return max(min(value, 1), -1)

    def gen_data(self):
        data = dict()
        pic_list = list()
        pic_list.append(self.solve())
        data['urls'] = pic_list
        return data


# 拉格朗日松弛算法
class Lagrange(BaseModel):
    def __init__(self):
        super().__init__()

    def work(self, n, m, h, c, lamda, f):
        b = [0] * m
        x = [0] * m
        y = [[0 for _ in range(m)] for _ in range(n)]
        for j in range(m):
            for i in range(n):
                b[j] += min(0, h[i] * c[i][j] - lamda[i])
            if b[j] + f[j] < 0:
                x[j] = 1
                for i in range(n):
                    if h[i] * c[i][j] - lamda[i] < 0:
                        y[i][j] = 1
                    else:
                        y[i][j] = 0
            else:
                x[j] = 0
                for i in range(n):
                    y[i][j] = 0
        
        z = 0
        for j in range(m):
            z += min(0, b[j] + f[j])
        for i in range(n):
            z += lamda[i]

        return x, y, z


    def find_solution(self, n, m, x_bar, yy, c, f, h):
        y = copy.deepcopy(yy)
        x = x_bar
        for i in range(n):
            pos_j = 0
            for j in range(1, m):
                if c[i][j] < c[i][pos_j] and x[j] == 1 or x[pos_j] == 0:
                    pos_j = j
            y[i][pos_j] = 1
            for j in range(m):
                if j != pos_j:
                    y[i][j] = 0

        z = 0
        for j in range(m):
            z += f[j] * x[j]
            for i in range(n):
                z += h[i] * c[i][j] * y[i][j]
        
        return x, y, z


    def lagrangian_relation(self, lamda, alpha, gama, k, iterlimit, n, m, h, c, f):
        t = 0
        LB = -math.inf
        UB = math.inf
        NonImprCtr = 0
        x_ans, y_ans = None, None
        while UB - LB > k and t <= iterlimit:
            x, y, z = self.work(n, m, h, c, lamda, f)
            if z > LB:
                LB = z
                NonImprCtr = 0
            else:
                NonImprCtr = NonImprCtr + 1
                if NonImprCtr == gama:
                    alpha = alpha / 2
                    NonImprCtr = 0
                else:
                    pass
            x2, y2, z2 = self.find_solution(n, m, x, y, c, f, h)
            if z2 < UB:
                UB = z2
                x_ans, y_ans = x2, y2
            print('iter', t,  'LB', LB, 'UB', UB, 'alpha', alpha)
            tmp = 0
            for i in range(n):
                tmp2 = 0
                for j in range(m):
                    tmp2 += y[i][j]
                tmp += (1 - tmp2) ** 2
            if tmp == 0:
                break
            delta = alpha * (UB - z) / tmp
            for i in range(n):
                tmp = 0
                for j in range(m):
                    tmp += y[i][j]
                lamda[i] += delta * (1 - tmp)
            t += 1
            fix_cost = 0
            for i in range(n):
                fix_cost += x_ans[i] * f[i]
        print('lamda', lamda)

        ans_dict = dict()
        ans_dict['x_ans'] = x_ans
        ans_dict['y_ans'] = y_ans
        ans_dict['UB'] = UB
        ans_dict['fix_cost'] = fix_cost
        ans_dict['UB - fix_cost'] = UB - fix_cost
        return ans_dict


    def get_value(self, sheet, x, y):
        if y < 26:
            s = chr(y + ord('A'))
        else:
            y -= 26
            s = chr(y // 26 + ord('A')) + chr(y % 26 + ord('A'))
        return sheet[s + str(x + 1)].value


    # read 88.xlsx
    def data_parse(self, file_path, n):
        data = dict()
        data['n'] = n
        m = n
        data['m'] = m
        data['lamda'] = [100000] * n
        data['alpha'] = 2
        data['gama'] = 20
        data['k'] = 0.00000001
        data['iterlimit'] = 10000
        wb = openpyxl.load_workbook(file_path, data_only=True)
        sheet = wb[wb.sheetnames[0]]
        f = list()
        for j in range(2, m + 2):
            f.append(float(self.get_value(sheet, j, 5)) * 10)
        data['f'] = f
        h = list()
        for i in range(2, n + 2):
            h.append(float(self.get_value(sheet, i, 3)) / 1000)
        data['h'] = h
        sheet = wb[wb.sheetnames[1]]
        c = list()
        for i in range(2, n + 2):
            tmp = list()
            for j in range(1, m + 1):
                tmp.append(float(self.get_value(sheet, i, j)))
            c.append(tmp)
        data['c'] = c
        return data

    def gen_data(self):
        data = dict()
        pic_list = list()
        data['urls'] = pic_list
        file_path = 'course/88node.xlsx'
        data.update(self.lagrangian_relation(**self.data_parse(file_path, 88)))
        return data


# 双上升算法
class Dual(BaseModel):
    def __init__(self):
        super().__init__()

    def dual_ascent(self, n, m, c, f, I, J):
        cc = copy.deepcopy(c)
        for i in I:
            cc[i].sort()
            cc[i].append(math.inf)
        v = [0] * n
        k = [0] * n
        for i in I:
            v[i] = cc[i][0]
            k[i] = 1
        s = [0] * m
        for j in J:
            s[j] = f[j]
        improved = True
        while improved:
            improved = False
            for i in I:
                delta = math.inf
                for j in J:
                    if v[i] - c[i][j] >= 0:
                        delta = min(delta, s[j])
                if delta >= cc[i][k[i]] - v[i]:
                    delta = cc[i][k[i]] - v[i]
                    improved = True
                    k[i] += 1
                for j in J:
                    if v[i] - c[i][j] >= 0:
                        s[j] -= delta
                v[i] += delta
        set_j = [0] * m
        for j in J:
            if s[j] == 0:
                set_j[j] = 1
        ZD = sum(v)
        ZP = 0
        for j in J:
            ZP += f[j] * set_j[j]
        y = [0] * n
        for i in I:
            pos = -1
            for j in J:
                if set_j[j] == 1 and (pos == -1 or c[i][j] < c[i][pos]):
                    pos = j
            y[i] = pos
            ZP += c[i][pos]
        return v, set_j, ZD, ZP, y, s


    def sub_dual_ascent(n, m, c, f, I, J, v, s):
        cc = copy.deepcopy(c)
        for i in I:
            cc[i].sort()
            cc[i].append(math.inf)
        k = [0] * n
        for i in I:
            k[i] = 1
        improved = True
        while improved:
            improved = False
            for i in I:
                delta = math.inf
                for j in J:
                    if v[i] - c[i][j] >= 0:
                        delta = min(delta, s[j])
                if delta >= cc[i][k[i]] - v[i]:
                    delta = cc[i][k[i]] - v[i]
                    improved = True
                    k[i] += 1
                for j in J:
                    if v[i] - c[i][j] >= 0:
                        s[j] -= delta
                v[i] += delta
        set_j = [0] * m
        for j in J:
            if s[j] == 0:
                set_j[j] = 1
        ZD = sum(v)
        ZP = 0
        for j in J:
            ZP += f[j] * set_j[j]
        y = [0] * n
        for i in I:
            pos = -1
            for j in J:
                if set_j[j] == 1 and (pos == -1 or c[i][j] < c[i][pos]):
                    pos = j
            y[i] = pos
            ZP += c[i][pos]
        return v, set_j, ZD, ZP, y, s


    def dual_adjustment(self, n, m, c, f, v, set_j, s):
        I = [i for i in range(n)]
        J = [j for j in range(m)]
        J_plus = list()
        for j in range(m):
            if set_j[j] == 1:
                J_plus.append(j)
        for i in I:
            improved = True
            while improved:
                improved = False
                cnt = 0
                for j in J_plus:
                    if v[i] > c[i][j]:
                        cnt += 1
                if cnt > 1:
                    I_plus = list()
                    for ii in I:
                        tmp_cnt = 0
                        for j in J_plus:
                            if v[ii] > c[ii][j]:
                                tmp_cnt += 1
                        if tmp_cnt == 2:
                            I_plus.append(ii)
                    if len(I_plus) != 0:
                        p = 0
                        for j in J:
                            if v[i] > c[i][j]:
                                p = max(p, c[i][j])
                        for j in J:
                            if v[i] > c[i][j]:
                                s[j] = s[j] + v[i] - p
                        v[i] = p
                        v, set_j, ZD, ZP, y, s = self.dual_ascent(n, m, c, f, I_plus, J)
                        # sub_dual_ascent(n, m, c, f, I_plus, J, v, s)
                        # I_plus.append(i)
                        # sub_dual_ascent(n, m, c, f, list(set(I_plus)), J, v, s)
                        v, set_j, ZD, ZP, y, s = self.sub_dual_ascent(n, m, c, f, I, J, v, s)
                        print('v =', v)
                        print('set_j =', set_j)
                        print('ZD =', ZD)
                        print('ZP =', ZP)
                        print('y =', y)
                        print('s =', s)

    def solve(self):
        n = 8
        m = 5
        I = [i for i in range(n)]
        J = [i for i in range(m)]
        c = [[120, 210, 180, 210, 170],
            [180, float('inf'), 190, 190, 150],
            [100, 150, 110, 150, 110],
            [float('inf'), 240, 195, 180, 150],
            [60, 55, 50, 65, 70],
            [float('inf'), 210, float('inf'), 120, 195],
            [180, 110, float('inf'), 160, 200],
            [float('inf'), 165, 195, 120, float('inf')]]
        f = [100, 70, 60, 110, 80]
        v, set_j, ZD, ZP, y, s = self.dual_ascent(n, m, c, f, I, J)
        print('case2:')
        print('v =', v)
        print('set_j =', set_j)
        print('ZD =', ZD)
        print('ZP =', ZP)
        print('y =', y)
        print('s =', s)
        print('after dual_adjustment:')
        self.dual_adjustment(n, m, c, f, v, set_j, s)
        f = [200, 200, 200, 400, 300]
        v, set_j, ZD, ZP, y, s = self.dual_ascent(n, m, c, f, I, J)
        ans = dict()
        ans['n'] = n
        ans['m'] = m
        ans['I'] = I
        ans['J'] = J
        ans['c'] = c
        ans['f'] = f
        ans['v'] = v
        ans['set_j'] = set_j
        ans['ZD'] = ZD
        ans['ZP'] = ZP
        ans['y'] = y
        ans['s'] = s
        return ans

    def gen_data(self):
        data = dict()
        pic_list = list()
        data['urls'] = pic_list
        data.update(self.solve())
        return data


# 启发式算法
class Heuristics(BaseModel):
    def __init__(self):
        super().__init__()

    def get_value(self, sheet, x, y):
        if y < 26:
            s = chr(y + ord('A'))
        else:
            y -= 26
            s = chr(y // 26 + ord('A')) + chr(y % 26 + ord('A'))
        return sheet[s + str(x + 1)].value

    # read 88.xlsx
    def data_parse(self, file_path, n):
        data = dict()
        data['n'] = n
        m = n
        data['m'] = m
        wb = openpyxl.load_workbook(file_path, data_only=True)
        sheet = wb[wb.sheetnames[0]]
        f = list()
        for j in range(2, m + 2):
            f.append(float(self.get_value(sheet, j, 5)) * 10)
        data['f'] = f
        h = list()
        for i in range(2, n + 2):
            h.append(float(self.get_value(sheet, i, 3)) / 1000)
        data['h'] = h
        sheet = wb[wb.sheetnames[1]]
        c = list()
        for i in range(2, n + 2):
            tmp = list()
            for j in range(1, m + 1):
                tmp.append(float(self.get_value(sheet, i, j)))
            c.append(tmp)
        data['c'] = c
        return data

    def heuristics(self, n, m, f, h, c):
        x = [0] * m
        z = math.inf
        improved = True
        while improved:
            improved = False
            z_star = math.inf
            pos = -1
            output = list()
            for j in range(m):
                if x[j] == 0:
                    x_tmp = copy.deepcopy(x)
                    x_tmp[j] = 1
                    z_tmp = 0
                    for jj in range(m):
                        z_tmp += x_tmp[jj] * f[jj]
                    for i in range(n):
                        val = math.inf
                        for jj in range(m):
                            if x_tmp[jj] == 1 and c[i][jj] < val:
                                val = c[i][jj]
                        z_tmp += val * h[i]
                    if z_tmp < z_star:
                        z_star = z_tmp
                        pos = j
                    output.append(z_tmp)
            output.sort()
            print(output)
            if z_star < z:
                improved = True
                x[pos] = 1
                z = z_star
        y = [[0 for _ in range(m)] for _ in range(n)]
        for i in range(n):
            pos = -1
            for j in range(m):
                if x[j] == 1 and (pos == -1 or c[i][j] < c[i][pos]):
                    pos = j
            y[i][pos] = 1
        return {'x': x, 'y': y, 'z': z}

    def gen_data(self):
        data = dict()
        pic_list = list()
        data['urls'] = pic_list
        file_path = 'course/88node.xlsx'
        data.update(self.heuristics(**self.data_parse(file_path, 88)))
        return data


def LP(m, clist, alist, blist, aeqlist, beqlist, all_x=()):
    c = np.array(clist)
    a = np.array(alist)
    b = np.array(blist)
    aeq = np.array(aeqlist)
    beq = np.array(beqlist)
    if m == 'min':
        res = optimize.linprog(c, a, b, aeq, beq, bounds=all_x)
        fun = res.fun
        x = res.x
    else:
        res = optimize.linprog(-c, a, b, aeq, beq, bounds=all_x)
        fun = -res.fun
        x = res.x
    return fun, x


class UFLPsolver:
    def __init__(self, n, m, c, f, h=None):
        self.n = n
        self.m = m
        self.c = c
        self.f = f
        if h:
            for i in range(n):
                for j in range(m):
                    self.f[i][j] *= h[i]

    def solve(self, x):
        mm = 'min'
        clist = list()
        for i in range(self.m):
            clist.append(self.f[i])
        for i in range(self.n):
            for j in range(self.m):
                clist.append(self.c[i][j])
        blist = list()
        alist = list()
        num = self.m + self.n * self.m
        for i in range(self.n):
            for j in range(self.m):
                tmp = [0] * num
                blist.append(0)
                tmp[j] = -1
                tmp[self.m + self.m * i + j] = 1
                alist.append(tmp)
        aeqlist = list()
        beqlist = list()
        for i in range(self.n):
            tmp = [0] * num
            for j in range(self.m):
                tmp[self.m + i * self.m + j] = 1
            beqlist.append(1)
            aeqlist.append(tmp)
        all_x = list()
        for i in range(num):
            if x[i]:
                all_x.append((x[i], x[i]))
            else:
                all_x.append((0, 1))
        value, sol = LP(mm, clist, alist, blist, aeqlist, beqlist, all_x)
        return value, sol


class BranchBoundSolver:
    def __init__(self, n, m, c, f):
        self.best_ans = math.inf
        self.best_sol = None
        self.n = n
        self.m = m
        self.c = c
        self.f = f
        self.solver = UFLPsolver(n, m, c, f)
        self.eps = 1e-5

    def dfs(self, depth, x):
        if depth >= self.m:
            return
        val, sol = self.solver.solve(x + [None] * self.n * self.m)
        if val > self.best_ans:
            return
        pos = -1
        for i in range(self.m):
            if self.eps < sol[i] < 1 - self.eps:
                pos = i
                break
        if pos == -1:
            if val < self.best_ans:
                self.best_ans = val
                self.best_sol = sol
            return
        x[pos] = 0
        self.dfs(depth + 1, x)
        x[pos] = 1
        self.dfs(depth + 1, x)

    def solve(self):
        self.dfs(depth=0, x=[None] * self.m)
        print('object value:', format(self.best_ans, '.2f'))
        for i in range(len(self.best_sol)):
            self.best_sol[i] = format(self.best_sol[i], '.2f')
        print('x:', self.best_sol[:self.m])
        y = list()
        for i in range(self.n):
            y.append(self.best_sol[self.m + i * self.m: self.m + (i + 1) * self.m].tolist())
        print('y:', y)
        ans = dict()
        ans['x'] = self.best_sol[:self.m].tolist()
        ans['y'] = y
        ans['ans'] = format(self.best_ans, '.2f')
        return ans

# 分支定界算法
class Branch(BaseModel):
    def __init__(self):
        super().__init__()

    def solve(self):

        INF = 10000000
        c = [[120, 210, 180, 210, 170],
            [180, INF, 190, 190, 150],
            [100, 150, 110, 150, 110],
            [INF, 240, 195, 180, 150],
            [60, 55, 50, 65, 70],
            [INF, 210, INF, 120, 195],
            [180, 110, INF, 160, 200],
            [INF, 165, 195, 120, INF]]

        f = [200, 200, 200, 400, 300]
        solver = BranchBoundSolver(n=8, m=5, c=c, f=f)
        return solver.solve() 

    def gen_data(self):
        data = dict()
        pic_list = list()
        data['urls'] = pic_list
        data.update(self.solve())
        return data


class ModelManager:
    def work(self, request):
        try:
            json_dict = json.loads(request.body)
        except Exception:
            return JsonResponse({'code': 500, 'message': '请求参数错误'})
        example_key = json_dict.get('example_key', None)
        param = json_dict.get('param', None)
        if example_key is None or param is None:
            return JsonResponse({'code': 500, 'message': '请求参数错误'})
        try:
            example = Example.objects.get(id=example_key)
        except Exception:
            return JsonResponse({'code': 500, 'message': '请求参数错误'})
        classname = example.mymodel.classname
        if classname == 'Test1':
            cla = Test1()
        elif classname == 'WholesalePriceContract':
            cla = WholesalePriceContract()
        elif classname == 'BuybackContract':
            cla = BuybackContract()
        elif classname == 'RevenueSharingContract':
            cla = RevenueSharingContract()
        elif classname == 'QuantityFlexibleContract':
            cla = QuantityFlexibleContract()
        elif classname == 'SaleRebateContract':
            cla = SaleRebateContract()
        elif classname == 'QuantityDiscountContract':
            cla = QuantityDiscountContract()
        elif classname == 'ProductionGame':
            cla = ProductionGame()
        elif classname == 'PricingGame':
            cla = PricingGame()
        elif classname == 'IntegerKnapsackProblem':
            cla = IntegerKnapsackProblem()
        elif classname == 'KeywordSearchAuction':
            cla = KeywordSearchAuction()
        elif classname == 'NimGame':
            cla = NimGame()
        elif classname == 'SphereDistance':
            cla = SphereDistance()
        elif classname == 'Lagrange':
            cla = Lagrange()
        elif classname == 'Dual':
            cla = Dual()
        elif classname == 'Heuristics':
            cla = Heuristics()
        elif classname == 'Branch':
            cla = Branch()
        else:
            raise TypeError
        cla.set_value(param)
        data = cla.gen_data()
        return JsonResponse({'code': 200, 'message': '请求成功', 'data': data})
